const mdns = require('multicast-dns');
const dgram = require('dgram');
const os = require('os');

class NetworkDiscovery {
  constructor() {
    this.mdns = mdns();
    this.udpSocket = dgram.createSocket('udp4');
    this.serviceName = '_lanchat._tcp.local';
    this.broadcastPort = 3002;
    this.httpPort = null;
    this.wsPort = null;
  }

  start(httpPort, wsPort) {
    this.httpPort = httpPort;
    this.wsPort = wsPort;

    this.setupMDNS();
    this.setupUDPBroadcast();

    console.log('Network discovery started');
  }

  setupMDNS() {
    // Announce service via mDNS
    this.mdns.on('query', (query) => {
      if (query.questions.some(q => q.name === this.serviceName)) {
        this.mdns.respond({
          answers: [{
            name: this.serviceName,
            type: 'PTR',
            data: `LANChat Server._lanchat._tcp.local`
          }, {
            name: `LANChat Server._lanchat._tcp.local`,
            type: 'SRV',
            data: {
              priority: 0,
              weight: 0,
              port: this.httpPort,
              target: this.getLocalIP()
            }
          }, {
            name: `LANChat Server._lanchat._tcp.local`,
            type: 'TXT',
            data: [
              `ws_port=${this.wsPort}`,
              `http_port=${this.httpPort}`,
              'version=1.0.0'
            ]
          }]
        });
      }
    });

    // Periodically announce presence
    this.announceInterval = setInterval(() => {
      this.mdns.query({
        questions: [{
          name: this.serviceName,
          type: 'PTR'
        }]
      });
    }, 30000); // Every 30 seconds
  }

  setupUDPBroadcast() {
    const localIP = this.getLocalIP();

    this.udpSocket.bind(this.broadcastPort, localIP, () => {
      this.udpSocket.setBroadcast(true);
      console.log(`UDP broadcast listening on ${localIP}:${this.broadcastPort}`);
    });

    // Listen for discovery requests
    this.udpSocket.on('message', (msg, rinfo) => {
      try {
        const data = JSON.parse(msg.toString());
        if (data.type === 'discovery-request') {
          this.respondToDiscovery(rinfo.address);
        }
      } catch (error) {
        // Ignore invalid messages
      }
    });

    // Broadcast presence periodically
    this.broadcastInterval = setInterval(() => {
      this.broadcastPresence();
    }, 15000); // Every 15 seconds
  }

  broadcastPresence() {
    const message = JSON.stringify({
      type: 'server-announce',
      httpPort: this.httpPort,
      wsPort: this.wsPort,
      ip: this.getLocalIP(),
      timestamp: Date.now()
    });

    const broadcastAddress = this.getBroadcastAddress();
    this.udpSocket.send(message, this.broadcastPort, broadcastAddress, (err) => {
      if (err) {
        console.error('Broadcast error:', err);
      }
    });
  }

  respondToDiscovery(clientAddress) {
    const response = JSON.stringify({
      type: 'discovery-response',
      httpPort: this.httpPort,
      wsPort: this.wsPort,
      ip: this.getLocalIP(),
      timestamp: Date.now()
    });

    this.udpSocket.send(response, this.broadcastPort, clientAddress, (err) => {
      if (err) {
        console.error('Discovery response error:', err);
      }
    });
  }

  getLocalIP() {
    const interfaces = os.networkInterfaces();
    for (const name of Object.keys(interfaces)) {
      for (const iface of interfaces[name]) {
        if (iface.family === 'IPv4' && !iface.internal) {
          return iface.address;
        }
      }
    }
    return '127.0.0.1';
  }

  getBroadcastAddress() {
    const interfaces = os.networkInterfaces();
    for (const name of Object.keys(interfaces)) {
      for (const iface of interfaces[name]) {
        if (iface.family === 'IPv4' && !iface.internal) {
          // Calculate broadcast address
          const ip = iface.address.split('.');
          const mask = iface.netmask.split('.');
          const broadcast = ip.map((octet, i) => {
            return (parseInt(octet) | (255 - parseInt(mask[i]))).toString();
          });
          return broadcast.join('.');
        }
      }
    }
    return '255.255.255.255';
  }

  stop() {
    if (this.announceInterval) {
      clearInterval(this.announceInterval);
    }
    if (this.broadcastInterval) {
      clearInterval(this.broadcastInterval);
    }
    this.mdns.destroy();
    this.udpSocket.close();
  }
}

module.exports = NetworkDiscovery;
